package com.ccp.dev.extend.controller;

import com.ccp.dev.core.basic.annotion.Action;
import com.ccp.dev.core.basic.consts.SystemConst;
import com.ccp.dev.core.basic.consts.UrlConstants;
import com.ccp.dev.core.basic.internationalization.SysBaseController;
import com.ccp.dev.core.basic.model.SysAuditModelType;
import com.ccp.dev.core.basic.response.ResultData;
import com.ccp.dev.core.basic.table.TableModel;
import com.ccp.dev.core.basic.util.ContextUtil;
import com.ccp.dev.core.basic.util.PropertyUtil;
import com.ccp.dev.core.basic.web.query.QueryFilter;
import com.ccp.dev.core.jms.IMessageHandler;
import com.ccp.dev.core.util.AppUtil;
import com.ccp.dev.core.util.BeanUtils;
import com.ccp.dev.core.util.RequestUtil;
import com.ccp.dev.core.util.StringUtil;
import com.ccp.dev.core.util.log.SysAuditThreadLocalHolder;
import com.ccp.dev.form.model.FormDef;
import com.ccp.dev.form.service.FormDefService;
import com.ccp.dev.form.service.FormRightsService;
import com.ccp.dev.form.util.PlatformType;
import com.ccp.dev.system.dao.SysUserDao;
import com.ccp.dev.system.model.*;
import com.ccp.dev.system.service.SysFileService;
import com.ccp.dev.system.util.SysRoleUtil;
import com.ccp.dev.workflow.bpmconst.BpmConst;
import com.ccp.dev.workflow.bpmutil.BpmUtil;
import com.ccp.dev.workflow.bpmutil.ServiceUtil;
import com.ccp.dev.workflow.controller.TaskController;
import com.ccp.dev.workflow.dao.ActDefModelDao;
import com.ccp.dev.workflow.dao.TaskHistoryDao;
import com.ccp.dev.workflow.model.*;
import com.ccp.dev.workflow.model.bpm.*;
import com.ccp.dev.workflow.service.*;
import com.ccp.dev.workflow.service.bpm.BpmNodeSignService;
import com.ccp.dev.workflow.service.bpm.BpmRunLogService;
import com.ccp.dev.workflow.service.bpm.TaskSignDataService;
import com.ccp.dev.workflow.service.bpm.impl.BpmFlowableService;
import com.ccp.dev.workflow.service.bpm.thread.MessageUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;

/**
 * ClassDescribe:流程任务管理控制类
 *
 * @author :fanyanyan
 * Date: 2019-07-10
 */
@RequestMapping(UrlConstants.PROJECT_ROOT + "/extend/task")
@Controller
@Action(ownermodel = SysAuditModelType.PROCESS_CENTER)
public class ExtendTaskController extends SysBaseController {
    private static final String SUCCESS = "operation.success";
    private static final String FAILURE = "operation.failure";

    @Resource
    private BpmService bpmService;
    @Resource
    private ProcessRunService processRunService;
    @Resource
    private TaskReadService taskReadService;
    @Resource
    private ActDefModelDao actDefModelDao;
    @Resource
    private TaskService taskService;
    @Resource
    private BpmNodeButtonService bpmNodeButtonService;
    @Resource
    private BpmFormRunService bpmFormRunService;
    @Resource
    private ActDefModelService actDefModelService;
    @Resource
    private BpmDefAuthorizeService bpmDefAuthorizeService;
    @Resource
    private BpmNodeSetService bpmNodeSetService;
    @Resource
    private FormHandlerService formHandlerService;
    @Resource
    private BpmRunLogService bpmRunLogService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private BpmNodeUserService bpmNodeUserService;
    @Resource
    private BpmTaskExeService bpmTaskExeService;
    @Resource
    private FormDefService formDefService;
    @Resource
    private CommuReceiverService commuReceiverService;
    @Resource
    private TaskOpinionService taskOpinionService;
    @Resource
    private TaskUserService taskUserService;
    @Resource
    private TaskSignDataService taskSignDataService;
    @Resource
    private BpmNodeSignService bpmNodeSignService;
    @Resource
    private TaskApprovalItemsService taskApprovalItemsService;
    @Resource
    private SysFileService sysFileService;
    @Resource
    private TaskReminderService reminderService;
    @Resource
    private BpmFlowableService bpmFlowableService;
    @Resource
    private SysUserDao sysUserDao;
    @Resource
    private TaskHistoryDao taskHistoryDao;
    @Resource
    private FormRightsService formRightsService;


    /**
     * 代办事宜
     *
     * @param model 视图模型对象
     * @return 页面路径
     */
    @RequestMapping(UrlConstants.VIEW_DISPATCH_PREFIX + "/pendingMatters")
    @Action(description = "代办事宜页面")
    public String pendingMattersView(Model model) {
        String warningSet = reminderService.getWaringSetJson();
        model.addAttribute("warningSet", warningSet);
        return "workflow/pendingMatters";
    }

    /**
     * 已办事宜页面跳转
     * @return 跳转路径
     */
    @GetMapping(UrlConstants.VIEW_DISPATCH_PREFIX + "/alreadyMatter")
    @Action(ownermodel=SysAuditModelType.PROCESS_CENTER,description ="查看已办事宜页面")
    public String viewAlreadyMatter() {
        return "workflow/alreadyMatter";
    }

    /**
     * 办结事宜页面跳转
     *
     * @return 跳转路径
     */
    @RequestMapping(UrlConstants.VIEW_DISPATCH_PREFIX + "/completedMatters")
    @Action(ownermodel=SysAuditModelType.PROCESS_CENTER,description ="查看办结事宜页面")
    public String viewCompletedMatters() {
        return "workflow/completedMatters";
    }


    /**
     * 任务处理界面，根据任务ID获取流程实例，根据流程实例获取表单数据。
     * @param request request
     * @param model 视图模型对象
     * @return 页面路径
     */
    @RequestMapping(UrlConstants.VIEW_DISPATCH_PREFIX + "/toStart")
    @Action(description = "任务处理页面")
    public String toStart(HttpServletRequest request, Model model){
        return getToStartView(request, model, 0);
    }


    /**
     * 任务处理页面
     * @param request request
     * @param model 视图模型对象
     * @param isManage 是否管理员，0：非管理员；1：管理员
     * @return 页面路径
     */
    private String getToStartView(HttpServletRequest request, Model model, int isManage) {
        try {
            SysUser sysUser = (SysUser) ContextUtil.getCurrentUser();
            String taskId = RequestUtil.getString(request, "taskId");
            String procInstId = RequestUtil.getString(request, "instanceId");
            String runId = RequestUtil.getString(request, "runId");
            //2020-03-09 jyq添加，是否是在线任务交接处单击事项名称访问的
            String handover = RequestUtil.getString(request, "handover");
            if (StringUtil.isEmpty(taskId) && StringUtil.isEmpty(procInstId)) {
                return ServiceUtil.getTipInfo(model, "没有输入任务或实例ID!");
            }
            // 根据流程实例获取流程任务。
            if (StringUtil.isEmpty(taskId) && StringUtil.isNotEmpty(procInstId)) {
                List<ProcessTask> list = bpmService.getTasks(procInstId);
                if (BeanUtils.isNotEmpty(list)) {
                    taskId = list.get(0).getId();
                }
            }
            // 查找任务节点
            TaskEntity taskEntity = bpmService.getTask(taskId);
            if (taskEntity == null) {
                ProcessTaskHistory taskHistory = taskHistoryDao.getById(taskId);
                if (taskHistory == null) {
                    if (StringUtil.isEmpty(taskId) && StringUtil.isEmpty(procInstId)) {
                        return ServiceUtil.getTipInfo(model, "任务ID错误!");
                    }
                } else {
                    String processInstanceId = taskHistory.getProcessInstanceId();
                    if (StringUtil.isEmpty(processInstanceId) && taskHistory.getDescription().equals(TaskOpinion.STATUS_COMMUNICATION.toString())) {
                        return ServiceUtil.getTipInfo(model, "此任务为沟通任务,并且此任务已经处理!");
                    }
                    ProcessRun processRun = processRunService.getByProcInstanceId(processInstanceId);
                    if (processRun == null) {
                        return ServiceUtil.getTipInfo(model, "任务不存在!");
                    }
                    String url = "/workflow/processRun/view/info?runId=" + processRun.getRunId();
                    return "redirect:" + url;
                }
                return null;
            }
            if (StringUtil.isEmpty(handover) && TaskOpinion.STATUS_TRANSTO_ING.toString().equals(taskEntity.getDescription()) && taskEntity.getAssignee().equals(sysUser.getUserId())) {
                return ServiceUtil.getTipInfo(model, "对不起,这个任务正在流转中,不能处理此任务!");
            }
            procInstId = taskEntity.getProcessInstanceId();

            if (isManage == 0) {
                boolean hasRights = processRunService.getHasRightsByTask(taskEntity.getId(), sysUser.getUserId());
                if (!hasRights) {
                    return ServiceUtil.getTipInfo(model, "对不起,你不是这个任务的执行人,不能处理此任务!");
                }
            }
            // 更新任务为已读。
            taskReadService.saveReadRecord(procInstId, taskId);
            // 设置沟通人员或流转人员查看状态。
            commuReceiverService.setCommuReceiverStatus(taskEntity, sysUser);

            String nodeId = taskEntity.getTaskDefinitionKey();
            String procDefId = taskEntity.getProcessDefinitionId();
            String userId = ContextUtil.getCurrentUserId();

            ActDefModel actDefModel = actDefModelDao.getByProcDefId(procDefId);
            ProcessRun processRun = processRunService.getByProcInstanceId(procInstId);
            String businessKey = processRun.getBusinessKey();
            boolean isFirstNode = NodeCache.isFirstNode(procDefId, nodeId);
            BpmNodeSet bpmNodeSet;
            String parentProcDefId = "";
            int dataPrivacyLevel = 1;
            Map<String, Object> variables = taskService.getVariables(taskId);
            if (variables.containsKey(BpmConst.FLOW_PARENT_PROCDEFID)) {
                // 判断当前是否属于子流程任务
                parentProcDefId = variables.get(BpmConst.FLOW_PARENT_PROCDEFID).toString();
                bpmNodeSet = bpmNodeSetService.getByProcDefIdNodeId(procDefId, nodeId, parentProcDefId);
            } else {
                bpmNodeSet = bpmNodeSetService.getByProcDefIdNodeId(procDefId, nodeId);
            }

            String toBackNodeId = "";
            if (StringUtil.isNotEmpty(processRun.getStartNode())) {
                toBackNodeId = processRun.getStartNode();
            } else {
                try {
                    toBackNodeId = NodeCache.getFirstNodeInfo(procDefId).getNodeId();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            String form = "";
            String tempLaunchId = userId;
            if (StringUtil.isEmpty(taskEntity.getExecutionId())) {
                if (taskEntity.getDescription().equals(TaskOpinion.STATUS_TRANSTO.toString())) {
                    List<TaskOpinion> taskOpinionList = taskOpinionService.getByProcInstId(procInstId);
                    if (BeanUtils.isNotEmpty(taskOpinionList)) {
                        TaskOpinion taskOpinion = taskOpinionList.get(taskOpinionList.size() - 1);
                        List<CommuReceiver> commuReceiverList = commuReceiverService.getByOpinionId(taskOpinion.getOpinionId());
                        if (BeanUtils.isNotEmpty(commuReceiverList)) {
                            tempLaunchId = taskOpinion.getExeUserId();
                        }
                    }
                }
            }

            int formType = bpmNodeSet.getFormType();
            //是否绑定表单
            Boolean isFormEmpty = formType == -1;
            //是否外部表单
            Boolean isExtForm = formType > 0;

            if (isExtForm) {
                //外部表单
                form = bpmNodeSet.getFormUrl().replaceFirst(BpmConst.FORM_PK_REGEX, businessKey);

            } else if (formType == 0) {
                //在线表单
                form = "/form/dataTemplate/view/addEdit?formKey=" + bpmNodeSet.getFormKey() + "&id=" + processRun.getBusinessKey() + "&procDefId=" + procDefId + "&nodeId=" + nodeId + "&hideBtn=yes" + "&needHeadAndBottom=no&enableEditPrivacy=0";
                // 获取密级传回前台.
                if (StringUtil.isNotEmpty(processRun.getBusinessKey())&& StringUtil.isNotEmpty(processRun.getDsAlias())) {
                    dataPrivacyLevel = formHandlerService.getPrivacyLevelByTable(processRun.getDsAlias(), processRun.getTableName(), processRun.getPkName(), processRun.getBusinessKey());
                }
            }
            // 是否会签任务
            boolean isSignTask = NodeCache.isSignTaskNode(procDefId, nodeId);
            if (isSignTask) {
                handleSignTask(model, procInstId, nodeId, procDefId, userId);
            }
            // 是否支持回退
            boolean isCanBack = false;
            //2019-11-07    新增判断，解决因为沟通任务可能不存在执行人ID（executionId）而导致页面错误500
            if (StringUtil.isNotEmpty(taskEntity.getExecutionId())) {
                isCanBack = bpmFlowableService.isTaskAllowBack(taskId);
            }
            // 是否转办
            boolean isCanAssignee;
            isCanAssignee = bpmTaskExeService.isAssigneeTask(taskEntity, actDefModel);

            // 是否隐藏路径
            boolean isHidePath = getIsHidePath(bpmNodeSet.getIsHidePath());

            // 是否是选择路径跳转
            boolean isHandChoose = false;
            if (!isHidePath) {
                boolean canChoicePath = bpmService.getCanChoicePath(procDefId, taskId);
                String curUserId = ContextUtil.getCurrentUserId();
                List<NodeTranUser> nodeTranUserList = bpmService.getNodeTaskUserMap(taskId, curUserId, canChoicePath);
                if (nodeTranUserList.size() > 1) {
                    isHandChoose = true;
                }
            }

            // 获取页面显示的按钮
            Map<String, List<BpmNodeButton>> mapButton = bpmNodeButtonService.getMapByDefNodeId(procDefId, nodeId);

            //获取消息通知类型
            Map handlersMap = ServiceUtil.getHandlerMap();
            //获取常用语
            List<String> taskAppItems = taskApprovalItemsService.getApprovalByDefKeyAndTypeId(actDefModel.getModelKey(), actDefModel.getTypeId());
            // 获取保存的意见
            TaskOpinion taskOpinion = taskOpinionService.getOpinionByTaskIdUserId(taskId, userId);
            // 帮助文档
            SysFile sysFile = null;
            if (BeanUtils.isNotEmpty(actDefModel.getAttachment())) {
                sysFile = sysFileService.getOneById(actDefModel.getAttachment());
            }
            // 是否有全局流水号
            boolean hasGlobalFlowNo = PropertyUtil.getBooleanByAlias(SysProperty.GLOBAL_FLOW_NO);
            model.addAttribute("task", taskEntity)
                    .addAttribute("runId", runId)
                    .addAttribute("procInstId", procInstId)
                    .addAttribute("taskId", taskId)
                    .addAttribute("bpmNodeSet", bpmNodeSet)
                    .addAttribute("processRun", processRun)
                    .addAttribute("actDefModel", actDefModel)
                    .addAttribute("isSignTask", isSignTask)
                    .addAttribute("toBackNodeId", toBackNodeId)
                    .addAttribute("isCanBack", isCanBack)
                    .addAttribute("isCanAssignee", isCanAssignee)
                    .addAttribute("isHidePath", isHidePath)
                    .addAttribute("mapButton", mapButton)
                    .addAttribute("isManage", isManage)
                    .addAttribute("isHandChoose", isHandChoose)
                    .addAttribute("handlersMap", handlersMap)
                    .addAttribute("curUserId", sysUser.getUserId())
                    .addAttribute("curUserName", sysUser.getFullName())
                    .addAttribute("formKey", bpmNodeSet.getFormKey())
                    .addAttribute("nodeId", nodeId)
                    .addAttribute("form", form)
                    .addAttribute("procDefId", procDefId)
                    .addAttribute("parentProcDefId", parentProcDefId)
                    .addAttribute("isExtForm", isExtForm)
                    .addAttribute("isFormEmpty", isFormEmpty)
                    .addAttribute("taskOpinion", taskOpinion)
                    .addAttribute("sysFile", sysFile)
                    .addAttribute("hasGlobalFlowNo", hasGlobalFlowNo)
                    .addAttribute("taskAppItems", taskAppItems)
                    .addAttribute("dataPrivacyLevel", dataPrivacyLevel)
                    .addAttribute("isFirstNode", isFirstNode)
                    .addAttribute("handover", handover);
            return "workflow/toStart";
        } catch (Exception e) {
            model.addAttribute("content", e.getCause());
            return "workflow/task/tipInfo";
        }
    }


    /**
     * 处理会签
     * @param model mvc对象
     * @param procInstId 流程实例id
     * @param nodeId 节点id
     * @param procDefId 流程定义id
     * @param userId 用户id
     */
    private void handleSignTask(Model model, String procInstId, String nodeId, String procDefId, String userId) {
        List<TaskSignData> signDataList = taskSignDataService.queryByNodeAndInstanceId(procInstId, nodeId);
        // 获取会签规则
        BpmNodeSign bpmNodeSign = bpmNodeSignService.getByProcDefIdAndNodeId(procDefId, nodeId);

        model.addAttribute("signDataList", signDataList);
        model.addAttribute("bpmNodeSign", bpmNodeSign);
        model.addAttribute("curUser", ContextUtil.getCurrentUser());

        // 获取当前组织id
        String orgId = ContextUtil.getCurrentOrgId();

        // "允许直接处理"特权
        boolean isAllowDirectExecute = bpmNodeSignService.checkNodeSignPrivilege(procDefId, nodeId, BpmNodeSignService.BpmNodePrivilegeType.ALL_DIRECT, userId, orgId);
        // "允许补签"特权
        boolean isAllowRetoactive = bpmNodeSignService.checkNodeSignPrivilege(procDefId, nodeId, BpmNodeSignService.BpmNodePrivilegeType.ALLOW_RETROACTIVE, userId, orgId);
        // "一票决断"特权
        boolean isAllowOneVote = bpmNodeSignService.checkNodeSignPrivilege(procDefId, nodeId, BpmNodeSignService.BpmNodePrivilegeType.ALLOW_ONE_VOTE, userId, orgId);
        model.addAttribute("isAllowDirectExecute", isAllowDirectExecute).addAttribute("isAllowRetoactive", isAllowRetoactive)
                .addAttribute("isAllowOneVote", isAllowOneVote);
    }

    /**
     * 是否执行隐藏路径
     * @param isHidePath 是否隐藏路径
     * @return boolean
     */
    private boolean getIsHidePath(Short isHidePath) {
        if (BeanUtils.isEmpty(isHidePath)) {
            return false;
        }
        if (BpmNodeSet.HIDE_PATH.shortValue() == isHidePath.shortValue()) {
            return true;
        }
        return false;
    }






}
